#pragma once

/// <summary>
/// Basic Shader: This shader implements standard lighting, texturing, alpha
/// transparency, texture animation, and fog. Up to three directional lights can be used.
/// <summary>

/**	Author: Dane Bulat
  * Created: 06/2013
  * Last Modified: 30/06/2013
  * Version:
  */

#include "common\d3dx11effect.h"
#include "common\LightHelper.h"		// Material and DirectionalLight.
#include "common\d3dUtil.h"			// Release macros.
#include "ShaderUtil.h"				// Compiling shader programs.

#include <D3D11.h>
#include <xnamath.h>
#include <string>

class BasicShader
{
//==============================
// Constructor
//==============================
	BasicShader();

	static BasicShader*		m_pEffect;			///< Pointer to singleton shader.
	static bool				m_instanceFlag;		///< Instance flag.

	ID3DX11Effect*			m_Effect;			///< Pointer to compiled effect.
	ID3D11InputLayout*		m_InputLayout;		///< Pointer to shader input layout.

public:
//==============================
// Destructor
//==============================
	~BasicShader();

//==============================
// Public Interface Methods
//==============================

	/**
	  * GetInstance
	  *
      * Getter to the single instance of this class.
	  */
	static BasicShader* GetInstance();
	
	/**
	  * Initialise
	  *
      * Initialise method that compiles the .hlsl file, binds the C++ variables, and create the input layout.
	  * @param Pointer to D3D Device
	  */
	void Initialise(ID3D11Device* device);

	/**
	  * Shutdown
	  *
      * Releases the necessary COM objects and dynamic memory allocations.
	  */
	void Shutdown();
	
	/**
	  * GetInputLayout
	  *
      * Getter that returns a pointer to the shader input layout.
	  */
	ID3D11InputLayout* GetInputLayout() const;

	/**
	  * SetWorldViewProj
	  *
      * Setter to set the shader world-view-projection matrix.
	  * @param World-view-projection matrix.
	  */
	void SetWorldViewProj(CXMMATRIX M);

	/**
	  * SetWorld
	  *
      * Setter to set the shader world matrix.
	  * @param World matrix.
	  */
	void SetWorld(CXMMATRIX M);

	/**
	  * SetWorldInvTranspose
	  *
      * Setter to set the shader world-inverse-transpose matrix.
	  * @param World-inverse-transpose matrix.
	  */
	void SetWorldInvTranspose(CXMMATRIX M);

	/**
	  * SetTexTransform
	  *
      * Setter to set the shader texture transform matrix.
	  * @param Texture transform matrix.
	  */
	void SetTexTransform(CXMMATRIX);

	/**
	  * SetEyePosW
	  *
      * Setter to set the shader eye position.
	  * @param Vector representing camera eye position.
	  */
	void SetEyePosW(const XMFLOAT3& v);

	/**
	  * SetFogColor
	  *
      * Setter to set the shader fog colour.
	  * @param Vector representing the fog colour.
	  */
	void SetFogColor(const XMVECTOR v);

	/**
	  * SetFogStart
	  *
      * Setter to set the starting fog distance relative to the camera.
	  * @param Float representing the starting fog distance from the camera.
	  */
	void SetFogStart(float f);

	/**
	  * SetFogRange
	  *
      * Setter to set the range of the fog.
	  * @param Float representing the range value of the fog.
	  */
	void SetFogRange(float f);

	/**
	  * SetDirLights
	  *
      * Setter to set the shader directional lights.
	  * @param Pointer to a maximum of three directional lights.
	  */
	void SetDirLights(const DirectionalLight* lights);

	/**
	  * SetMaterial
	  *
      * Setter to set the shader material.
	  * @param Material that the shader will use to render geometry.
	  */
	void SetMaterial(const Material& mat);

	/**
	  * SetDiffuseMap
	  *
      * Setter to set the shader diffuse texture.
	  * @param ShaderResourceView that the shader will use to texture geometry.
	  */
	void SetDiffuseMap(ID3D11ShaderResourceView* tex);

private:

//==============================
// Private Methods
//==============================

	/**
	  * BindVariables
	  *
      * Binds the class C++ variables to the shader variables stored on the GPU.
	  */
	void BindVariables();

	/**
	  * CreateInputLayout
	  *
      * Creates the vertex input layout based on the vertex shader signature.
	  * @param Pointer to D3D Device
	  */
	void CreateInputLayout(ID3D11Device* device);

public:								// Public instead of Getter methods.
	// Techniques: Lighting
	ID3DX11EffectTechnique* Light1Tech;							///< Technique utilising 1 light.
	ID3DX11EffectTechnique* Light2Tech;							///< Technique utilising 2 lights.
	ID3DX11EffectTechnique* Light3Tech;							///< Technique utilising 3 lights.

	// Techniques: Lighting and Texturing
	ID3DX11EffectTechnique* Light0TexTech;						///< Technique utilising 0 lights and textures.
	ID3DX11EffectTechnique* Light1TexTech;						///< Technique utilising 1 light and textures.
	ID3DX11EffectTechnique* Light2TexTech;						///< Technique utilising 2 lights and textures.
	ID3DX11EffectTechnique* Light3TexTech;						///< Technique utilising 3 lights and textures.

	// Techniques: Lighting, Texturing and Alpha.
	ID3DX11EffectTechnique* Light0TexAlphaClipTech;				///< Technique utilising 0 lights, textures, and alpha clipping.
	ID3DX11EffectTechnique* Light1TexAlphaClipTech;				///< Technique utilising 1 light, textures, and alpha clipping.
	ID3DX11EffectTechnique* Light2TexAlphaClipTech;				///< Technique utilising 2 lights, textures, and alpha clipping.
	ID3DX11EffectTechnique* Light3TexAlphaClipTech;				///< Technique utilising 3 lights, textures, and alpha clipping.

	// Techniques: Lighting and Fog.
	ID3DX11EffectTechnique* Light1FogTech;						///< Technique utilising 1 light and fog.
	ID3DX11EffectTechnique* Light2FogTech;						///< Technique utilising 2 lights and fog.
	ID3DX11EffectTechnique* Light3FogTech;						///< Technique utilising 3 lights and fog.

	// Techniques: Lighting, Texturing and Fog.
	ID3DX11EffectTechnique* Light0TexFogTech;					///< Technique utilising 0 lights, textures and fog.
	ID3DX11EffectTechnique* Light1TexFogTech;					///< Technique utilising 1 light, textures and fog.
	ID3DX11EffectTechnique* Light2TexFogTech;					///< Technique utilising 2 lights, textures and fog.
	ID3DX11EffectTechnique* Light3TexFogTech;					///< Technique utilising 3 lights, textures and fog.

	// Techniques: Lighting, Texturing, Alpha, and Fog.
	ID3DX11EffectTechnique* Light0TexAlphaClipFogTech;			///< Technique utilising 0 lights, textures, alpha clipping and fog.
	ID3DX11EffectTechnique* Light1TexAlphaClipFogTech;			///< Technique utilising 1 light, textures, alpha clipping and fog.
	ID3DX11EffectTechnique* Light2TexAlphaClipFogTech;			///< Technique utilising 2 lights, textures, alpha clipping and fog.
	ID3DX11EffectTechnique* Light3TexAlphaClipFogTech;			///< Technique utilising 3 lights, textures, alpha clipping and fog.

	// Remaining Effect Variables.
	ID3DX11EffectMatrixVariable* WorldViewProj;					///< Shader world-view-projection matrix.
	ID3DX11EffectMatrixVariable* World;							///< Shader world matrix.
	ID3DX11EffectMatrixVariable* WorldInvTranspose;				///< Shader world-inverse-transpose-matrix.
	ID3DX11EffectMatrixVariable* TexTransform;					///< Shader texture transform matrix.
	ID3DX11EffectVectorVariable* EyePosW;						///< Shader eye position vector.

	ID3DX11EffectVectorVariable* FogColor;						///< Shader fog colour.
	ID3DX11EffectScalarVariable* FogStart;						///< Shader fog starting distance relative to the camera.
	ID3DX11EffectScalarVariable* FogRange;						///< Shader fog range.

	ID3DX11EffectVariable* DirLights;							///< Shader directional lights.
	ID3DX11EffectVariable* Mat;									///< Shader material.
	
	ID3DX11EffectShaderResourceVariable* DiffuseMap;			///< Shader diffuse texture.
	
	struct Vertex // Vertex structure for Basic.hlsl
	{
		Vertex() : Pos(0.0f, 0.0f, 0.0f), Normal(0.0f, 0.0f, 0.0f), Tex(0.0f, 0.0f) {}

		Vertex(const XMFLOAT3& p, const XMFLOAT3& n, const XMFLOAT2& uv)
			: Pos(p), Normal(n), Tex(uv) {}

		Vertex(float px, float py, float pz, float nx, float ny, float nz, float u, float v)
			: Pos(px, py, pz), Normal(nx, ny, nz), Tex(u, v) {}

		XMFLOAT3 Pos;
		XMFLOAT3 Normal;
		XMFLOAT2 Tex;
	};
};